//
//  Benchmark.h
//  GCDBenchmark
//
//  Created by 郭晓龙 on 2025/10/9.
//

#ifndef Benchmark_h
#define Benchmark_h

#include <vector>
#include <mutex>
#include <condition_variable>
#include "util.h"

inline void fork_join_pattern(size_t type, size_t cnt, size_t cost, QoSGenerator& qos)
{
    dispatch_group_t g = dispatch_group_create();
    for (size_t i = 0; i < cnt; i++) {
        uint64_t tsc = arm64_cnt_vct();
        dispatch_group_async(g, dispatch_get_global_queue(qos, 0), ^{
            TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
            run_task(type, cost);
        });
    }
    dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
}

inline void fibonacci(int x, size_t type, size_t cost, QoSGenerator& qos, dispatch_group_t group)
{
    if (x <= 1) {
        run_task(type, cost);
        return;
    }

    // 递归调用 fibonacci(x-1)
    uint64_t tsc = arm64_cnt_vct();
    dispatch_group_async(group, dispatch_get_global_queue(qos, 0), ^{
        TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
        fibonacci(x - 1, type, cost, qos, group);
    });

    // 递归调用 fibonacci(x-2)
    tsc = arm64_cnt_vct();
    dispatch_group_async(group, dispatch_get_global_queue(qos, 0), ^{
        TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
        fibonacci(x - 2, type, cost, qos, group);
    });

    run_task(type, cost);
}

inline void ariaw_pattern_nsoperation(size_t type, size_t buffer_cnt, size_t slice_cnt, size_t cost, QoSGenerator& qos)
{
    qos_class_t qos_class = qos;
    NSQualityOfService nsqos = (NSQualityOfService)qos_class;
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.qualityOfService = nsqos;
    
    NSMutableArray *post_ops = [NSMutableArray arrayWithCapacity:buffer_cnt];
    
    for (size_t i = 0; i < buffer_cnt; i++) {
        post_ops[i] = [NSNull null];
    }
    
    for (size_t i = 0; i < slice_cnt; i++) {
        size_t buf_id = i % buffer_cnt;

        uint64_t tsc = arm64_cnt_vct();
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
            run_task(type, cost);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
            run_task(type, cost);
        }];
        [op2 addDependency:op1];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
            run_task(type, cost);
        }];
        [op3 addDependency:op2];
        
        // 跨迭代依赖（关键）
        if (post_ops[buf_id] != (id)[NSNull null]) {
            [op1 addDependency:post_ops[buf_id]];
        }
        
        post_ops[buf_id] = op3;
        
        [queue addOperations:@[op1, op2, op3] waitUntilFinished:NO];
    }
    
    [queue waitUntilAllOperationsAreFinished];
}

inline void ariaw_pattern_semaphore(size_t type, size_t buffer_cnt, size_t slice_cnt, size_t cost, QoSGenerator& qos)
{
    qos_class_t qos_class = qos;
    dispatch_queue_t queue = dispatch_get_global_queue(qos_class, 0);
    dispatch_group_t group = dispatch_group_create();
    
    // 直接用dispatch_semaphore_t数组
    __strong dispatch_semaphore_t *pre_sems = (__strong dispatch_semaphore_t *)calloc(buffer_cnt, sizeof(dispatch_semaphore_t));
    __strong dispatch_semaphore_t *mid_sems = (__strong dispatch_semaphore_t *)calloc(buffer_cnt, sizeof(dispatch_semaphore_t));
    __strong dispatch_semaphore_t *iter_sems = (__strong dispatch_semaphore_t *)calloc(buffer_cnt, sizeof(dispatch_semaphore_t));
    
    for (size_t i = 0; i < buffer_cnt; i++) {
        pre_sems[i] = dispatch_semaphore_create(0);
        mid_sems[i] = dispatch_semaphore_create(0);
        iter_sems[i] = dispatch_semaphore_create(1);
    }
    
    for (size_t i = 0; i < slice_cnt; i++) {
        size_t buf_id = i % buffer_cnt;
        
        dispatch_semaphore_t pre_sem = pre_sems[buf_id];
        dispatch_semaphore_t mid_sem = mid_sems[buf_id];
        dispatch_semaphore_t iter_sem = iter_sems[buf_id];
        
        // Task 1
        uint64_t tsc = arm64_cnt_vct();
        dispatch_group_async(group, queue, ^{
            TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
            dispatch_semaphore_wait(iter_sem, DISPATCH_TIME_FOREVER);
            run_task(type, cost);
            dispatch_semaphore_signal(pre_sem);
        });
        
        // Task 2
        tsc = arm64_cnt_vct();
        dispatch_group_async(group, queue, ^{
            TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
            dispatch_semaphore_wait(pre_sem, DISPATCH_TIME_FOREVER);
            run_task(type, cost);
            dispatch_semaphore_signal(mid_sem);
        });
        
        // Task 3
        tsc = arm64_cnt_vct();
        dispatch_group_async(group, queue, ^{
            TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
            dispatch_semaphore_wait(mid_sem, DISPATCH_TIME_FOREVER);
            run_task(type, cost);
            dispatch_semaphore_signal(iter_sem);
        });
    }
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

    free(pre_sems);
    free(mid_sems);
    free(iter_sems);
}

// 串行队列
class SerialQueue {
public:
    SerialQueue(qos_class_t qos) : qos(qos)
    {
        queue = dispatch_queue_create("com.example.queue", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, qos, 0));
    }
    
    ~SerialQueue() = default;
    
    void Submit(void (^block)(void), qos_class_t qos)
    {
        dispatch_block_t _block = dispatch_block_create_with_qos_class(
            DISPATCH_BLOCK_ENFORCE_QOS_CLASS,
            qos,
            0,
            block
        );
        dispatch_async(queue, _block);
    }
    
    void Submit(std::function<void()> func, qos_class_t qos)
    {
        uint64_t tsc = arm64_cnt_vct();
        Submit(^{
            TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
            func();
        }, qos);
    }
    
    void Wait()
    {
        dispatch_sync(queue, ^{});
    }
private:
    dispatch_queue_t queue;
    qos_class_t qos;
};

// 并发队列
class ConcurrentQueue {
public:
    ConcurrentQueue(qos_class_t qos, int concurrency)
    {
        queue = [[NSOperationQueue alloc] init];
        [queue setMaxConcurrentOperationCount:concurrency];
        [queue setUnderlyingQueue:dispatch_get_global_queue(qos, 0)];
    }
    
    ~ConcurrentQueue()
    {
        queue = nil;
    }
    
    void Submit(void (^block)(void), qos_class_t qos)
    {
        NSOperation* operation = [NSBlockOperation blockOperationWithBlock:block];
        NSQualityOfService qos_ = (NSQualityOfService)qos;
        if (qos == QOS_CLASS_DEFAULT) {
            qos_ = NSQualityOfServiceDefault;
        }
        [operation setQualityOfService:qos_];
        [queue addOperationWithBlock:block];
    }
    
    void Submit(std::function<void()> func, qos_class_t qos)
    {
        uint64_t tsc = arm64_cnt_vct();
        Submit(^{
            TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
            func();
        }, qos);
    }
    
    void Wait()
    {
        [queue waitUntilAllOperationsAreFinished];
    }
private:
    NSOperationQueue* queue;
};

class Periodic {
public:
    Periodic(size_t type, size_t cnt, size_t cost, qos_class_t qos = QOS_CLASS_UNSPECIFIED)
        : m_type(type), m_cnt(cnt), m_cost(cost), m_qos(qos), group(dispatch_group_create())
    {
    }
    
    void Run()
    {
        for (size_t i = 0; i < m_cnt; i++) {
            uint64_t tsc = arm64_cnt_vct();
            dispatch_group_async(group, dispatch_get_global_queue(m_qos, 0), ^{
                TRACE_COUNT("cnt_delay", arm64_cnt_vct() - tsc);
                run_task(m_type, m_cost);
            });
            std::unique_lock lk(m_mtx);
            std::this_thread::sleep_for(std::chrono::microseconds(10000));
        }
        
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    }
    
    ~Periodic()
    {
    }

private:

    dispatch_group_t group;
    std::mutex m_mtx;
    std::condition_variable m_cv;
    bool m_exit { false };
    std::unique_ptr<std::thread> m_thread { nullptr };
    size_t m_type { 0 };
    size_t m_cnt { 0 };
    size_t m_cost { 0 };
    QoSGenerator m_qos;
};

#endif /* Benchmark_h */
